为Bochs添加新的虚拟硬盘格式

您所在的位置:网站首页 bochs win95镜像怎么用 为Bochs添加新的虚拟硬盘格式

为Bochs添加新的虚拟硬盘格式

2023-09-08 01:07| 来源: 网络整理| 查看: 265

1.1、问题的引出

​ 对于虚拟机来说,硬盘其实就是一个文件。虚拟机里面的操作系统对硬盘的所有操作都是对该硬盘的操作。说白了就是虚拟机欺骗的操作系统,让它认贼作父。当然这个贼并没有任何的恶意,相反对于用户来说,反而是好事。比如我们备份虚拟机的硬盘就非常方便,把虚拟硬盘文件备份一下就可以了。当然,虚拟机也可以使用真实硬盘。这可是千真万确的呦。但是,很不幸的是Bochs并没有提供这种支持。 ​ 当然啦,大多数情况下,我们使用Bochs都是用来调试自己写的小操作系统。所以,也没有使用真实硬盘的需求。但是,对于各种虚拟机来说,都自成一家,分别定义了自己的虚拟硬盘文件格式。这样一来,我们使用VBox安装的操作系统就无法在Qemu里面使用,当然也无法在Bochs里面使用。当然,我们完全可以在Qemu里面再装一个嘛。硬盘空间问题先不说,Bochs里面怎么安装个Windows Xp需要多长时间啊。真的是很难以估算啊(因为我还从来没有尝试成功过,即便是我耐心的等了两三个小时)。而,有时候我们又特别想看看Windows XP的引导过程,怎么办呢?调试一下呗。用什么调试?当然是Bochs啦。但是,怎么在Bochs里面安装Windows XP啊。 很早的时候,我们想了一个办法。那就是用Qemu安装,然后再用Bochs来运行并调试。之所以这样做是因为,Qemu的一种虚拟硬盘格式和Bochs是相同的。那就是Flat类型。但是,如果想看看Vista的呢?怎么办?还用老办法?告诉你,行不通喽。因为在Qemu里面安装不了Vista(原因不再我们的讨论之列)。 怎么办呢?是不是没解了? ​ 怎么会没解呢?别忘了Bochs可是开源的。开源就意味着你完全可以对Bochs进行改造。既然它原本不支持,那么就给他添加上嘛?(会不会很难呢?接着看吧。)

1.2、Bochs现在已经支持的硬盘格式

​ Bochs已经支持flat, concat, external, dll, sparse, vmware3, vmware4, undoable, growing, volatile, z-undoable, z-volatile等12种之多。对于各种类型的详细描述可以查看Bochs的使用手册。我们经常使用的应该就是flat和growing。不过要说的是上面列出的类型中很多已经被禁用掉了。比如dll类型,这种类型就是通过一个dll去读写一个虚拟硬盘文件。只是很可惜,该类型被禁用掉了。原因我就清楚了。因为,即便是打开这个宏后就无法编译成功。那么,我们不妨给它添加一种新的类型。

1.3、为Bochs添加一种新的硬盘格式

​ 对于每个虚拟机都会有一个配置文件(例如bochsrc.bxrc)。其中,会有一行描述了硬盘的信息: ata0-master: type=disk, mode=flat, path="c.img", cylinders=615, heads=6, spt=17 这一行的意思就是第1个IDE接口上的主硬盘(ata0-master)插的是一个硬盘(type=disk),其类型是平坦型(mode=flat)的,虚拟硬盘文件是c.img,该硬盘的CHS参数为:615,6,17。 ​ 于是,Bochs启动的时候就会执行如下代码: 【iodev\harddrv.cpp==>void bx_hard_drive_c::init(void)第282行】

if (SIM->get_param_enum("type", base)->get() == BX_ATA_DEVICE_DISK) { BX_DEBUG(("Hard-Disk on target %d/%d",channel,device)); BX_HD_THIS channels[channel].drives[device].device_type = IDE_DISK; sprintf(sbtext, "HD:%d-%s", channel, device?"S":"M"); BX_HD_THIS channels[channel].drives[device].statusbar_id = bx_gui->register_statusitem(sbtext); int cyl = SIM->get_param_num("cylinders", base)->get(); int heads = SIM->get_param_num("heads", base)->get(); int spt = SIM->get_param_num("spt", base)->get(); Bit64u disk_size = (Bit64u)cyl * heads * spt * 512; /* instantiate the right class */ image_mode = SIM->get_param_enum("mode", base)->get(); switch (image_mode) { case BX_ATA_MODE_FLAT: BX_INFO(("HD on ata%d-%d: '%s' 'flat' mode ", channel, device, SIM->get_param_string("path", base)->getptr())); channels[channel].drives[device].hard_drive = new default_image_t(); break; ...... default: BX_PANIC(("HD on ata%d-%d: '%s' unsupported HD mode : %s:%d", channel, device, SIM->get_param_string("path", base)->getptr(), atadevice_mode_names[image_mode])); break; }

​ 上面的代码的意思很清楚,就是从配置文件中读出配置信息,如CHS参数。然后根据硬盘模式判断到底是哪种类型。然后将该硬盘的hard_drive指向相关的类。比如Flat类型的是default_image_t,Growing就是growing_image_t……而这些类又都有一个相同的基类device_image_t。所以,无论格式如何变化,读写接口都是相同的。如果找不到呢?那就通知用户,您的虚拟硬盘类型不支持。 ​ 现在应该就很明了了。如果我们想添加一种新的格式,只需要新定义一种类型。然后对应的对应一个相关的访问类即可。 ​ 考虑到,我们的虚拟硬盘格式可能会复杂多变。我们不妨把读写文件的代码写到一个Dll里面。这样,以后当我们需要变换硬盘格式时就很简单了。只需要把我们写的那个Dll换一下就可以了。那么,如果我们想扩展多个类型呢?反不能每多一种都改一次Bochs的源码吧?解决起来很简单,我们只需要把Dll的名字和硬盘的类型名结合起来,然后在Default里面进行处理,再通过类型名加载对应的Dll不就可以了。例如我们添加VBoxGrowing类型的硬盘其Dll就叫做VBoxGrowing.dll。为了防止重名,我们规定这些Dll都被放在程序所在的VDisk子目录下。所以,我们将上面的代码修改如下:

...... default: BX_INFO(("HD on ata%d-%d: '%s' '%s' mode ", channel, device, SIM->get_param_string("path", base)->getptr())); channels[channel].drives[device].hard_drive = new dll2_image_t(SIM->get_param_string("path", base)->getptr()); break; }

​ 注意,上面所说的仅仅是主要改动。并不是全部改动。其实,主要是对配置文件的读取部分修改了一点,将mode的enum类型改成了string类型。 ​ dll2_image_t就是我们用来调用对应Dll的类。定义如下:

typedef int (*dll2_open)(const char * pathname, int flag); typedef void (*dll2_close)(); typedef ULONG64 (*dll2_seek)(ULONG64 offset, int whence); typedef ULONG64 (*dll2_size)(); typedef long (*dll2_read)(void* buf, size_t count); typedef long (*dll2_write)(const void* buf, size_t count); class dll2_image_t : public device_image_t { public: dll2_image_t(const char* dllName); ~dll2_image_t(); // Open a image. Returns non-negative if successful. int open(const char* pathname){return open(pathname, O_RDWR);}; // Open an image with specific flags. Returns non-negative if successful. int open(const char* pathname, int flags); // Close the image. void close(){m_close();}; // Position ourselves. Return the resulting offset from the // beginning of the file. Bit64s lseek(Bit64s offset, int whence){return m_seek(offset, whence);}; // Read count bytes to the buffer buf. Return the number of // bytes read (count). ssize_t read(void* buf, size_t count){return m_read(buf, count);}; // Write count bytes from buf. Return the number of bytes // written (count). ssize_t write(const void* buf, size_t count){return m_write(buf, count);}; private: dll2_open m_open; dll2_close m_close; dll2_seek m_seek; dll2_read m_read; dll2_write m_write; HMODULE m_hDll; }; dll2_image_t::dll2_image_t(const char* dllName):device_image_t() { char szDllName[MAX_PATH] = {0}; sprintf(szDllName, "vdisk\\%s.dll", dllName); m_hDll = LoadLibrary(szDllName); if (m_hDll) { m_open = (dll2_open)GetProcAddress(m_hDll, "vdOpen"); if (!m_open) { BX_PANIC(("No vdOpen() in vdisk.dll!")); } m_close = (dll2_close)GetProcAddress(m_hDll, "vdClose"); if (!m_close) { BX_PANIC(("No vdClose() in vdisk.dll!")); } m_seek = (dll2_seek)GetProcAddress(m_hDll, "vdSeek"); if (!m_seek) { BX_PANIC(("No vdSeek() in vdisk.dll!")); } m_read = (dll2_read)GetProcAddress(m_hDll, "vdRead"); if (!m_read) { BX_PANIC(("No vdRead() in vdisk.dll!")); } m_write = (dll2_write)GetProcAddress(m_hDll, "vdWrite"); if (!m_write) { BX_PANIC(("No vdWrite() in vdisk.dll!")); } } else { BX_PANIC(("Can't find out Hard Drive DLL:%s!", szDllName)); m_open = NULL; m_close = NULL; m_seek = NULL; m_read = NULL; m_write = NULL; } } dll2_image_t::~dll2_image_t() { if (m_hDll) { FreeLibrary(m_hDll); m_hDll = NULL; } } int dll2_image_t::open(const char* pathname, int flag) { if (!m_open) { return -1; } int iRet = m_open(pathname, flag); if (iRet < 0) { BX_ERROR(("Open file(%s:%d) failed!", pathname, flag)); } dll2_size size = (dll2_size)GetProcAddress(m_hDll, "vdSize"); if (!size) { BX_PANIC(("No vdSize() in vdisk.dll!")); return -1; } hd_size = size(); return iRet; }

​ 代码非常简单。就不再详细描述了。基本逻辑就是通过类型名找到对应的Dll并导出相关的函数。然后,依次调用而已。 ​ 下面我们再看看一个简单的硬盘类型格式,其实就是Flat格式。我们不妨定义它为myFlat类型。

#include #include HANDLE g_hFile = NULL; char g_szFileName[MAX_PATH] = {0}; #define LOG_FILE "myFlat.log" void LOG(LPTSTR lpszFormat, ...) { va_list arg; char szBuf[1024] = {0}; va_start (arg, lpszFormat); vsprintf (szBuf, lpszFormat, arg); va_end (arg); OutputDebugString(szBuf); FILE * f = fopen(LOG_FILE, "a+"); if (f) { fwrite(szBuf, 1, strlen(szBuf), f); fclose(f); f = NULL; } } int vdOpen(const char * pathname, int flag) { g_hFile = CreateFile(pathname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); if (g_hFile == INVALID_HANDLE_VALUE) { LOG("myFlat::Open file %s failed!flag=%d, ErrorNO=%ld", pathname, flag, GetLastError()); return -1; } strcpy(g_szFileName, pathname); return (int)g_hFile; } void vdClose() { if (g_hFile != INVALID_HANDLE_VALUE) { CloseHandle(g_hFile); g_hFile = INVALID_HANDLE_VALUE; memset(g_szFileName, 0, sizeof(g_szFileName)); } } ULONG64 vdSeek(ULONG64 offset, int whence) { LARGE_INTEGER liOffset; liOffset.QuadPart = offset; liOffset.LowPart = SetFilePointer(g_hFile, liOffset.LowPart, &liOffset.HighPart, whence); if (liOffset.LowPart == INVALID_FILE_SIZE) { LOG("vdisk::SetFilePointer(%s, %I64u, %d) failed!", g_szFileName, liOffset.QuadPart, whence); } return liOffset.QuadPart; } #define HDDEV_HEAD "\\\\.\\physicaldrive" ULONG64 vdSize() { if (strncmp(g_szFileName, HDDEV_HEAD, strlen(HDDEV_HEAD)) == 0) { // TODO:支持获取的大小硬盘 return 0; } DWORD dwHigh = 0; LARGE_INTEGER liSize; liSize.LowPart = GetFileSize(g_hFile, &dwHigh); if (liSize.LowPart == INVALID_FILE_SIZE) { LOG("myFlat::GetFileSize(%s) failed!", g_szFileName); return 0; } return liSize.QuadPart; } long vdRead(void* buf, size_t count) { DWORD dwRead = 0; if (!ReadFile(g_hFile, buf, count, &dwRead, 0)) { LOG("myFlat::ReadFile(%s, %lu) failed!", g_hFile, count); return 0; } return dwRead; } long vdWrite(const void* buf, size_t count) { DWORD dwWrite = 0; if (!WriteFile(g_hFile, buf, count, &dwWrite, 0)) { LOG("myFlat::WriteFile(%s, %lu) failed!", g_hFile, count); return 0; } return dwWrite; }

​ 同样,我也不对这段代码进行详细的注解(它们实在是太简单了,不是吗?)。需要说的是,这个格式可是支持真实硬盘的,不信的话你就把你的虚拟硬盘文件指定为:\.\physicaldrive0试一下。如果CHS参数正确的话,你肯定可以看到你操作系统的引导画面。不过,这样做是有一定危险的喔。你想想啊,你现在正在运行着的系统在哪儿啊?不就是在\.\physicaldrive0这个文件里面吗?如果再在虚拟机里面运行一遍会怎样呢?可以告诉的是,我试过,且已经很到了Windows的引导进度条,并且没有造成恶劣后果(但是,我可不保证你能和我一样好运)。

​ 怎么样?很简单吧。源码之下了无秘密。那就赶紧动手吧。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3